Add API to directly link() objects between repositories
authorColin Walters <walters@verbum.org>
Thu, 2 Oct 2014 03:19:31 +0000 (23:19 -0400)
committerColin Walters <walters@verbum.org>
Thu, 2 Oct 2014 03:20:35 +0000 (23:20 -0400)
And use it in pull-local.  As one might expect, this is blazingly fast
if they're on the same filesystem.

I'll be using this to "promote" builds between different repositories.

src/libostree/ostree-repo.c
src/libostree/ostree-repo.h
src/ostree/ot-builtin-pull-local.c

index 7069d01d6221aef215ffd9cc6f228a0bc7f16e71..0d24a9674f705af2ff05a83fafffa018b2f8c241 100644 (file)
@@ -1632,6 +1632,181 @@ ostree_repo_delete_object (OstreeRepo           *self,
   return ret;
 }
 
+static gboolean
+copy_detached_metadata (OstreeRepo    *self,
+                        OstreeRepo    *source,
+                        const char   *checksum,
+                        GCancellable  *cancellable,
+                        GError        **error)
+{
+  gboolean ret = FALSE;
+  gs_unref_variant GVariant *detached_meta = NULL;
+          
+  if (!ostree_repo_read_commit_detached_metadata (source,
+                                                  checksum, &detached_meta,
+                                                  cancellable, error))
+    goto out;
+
+  if (detached_meta)
+    {
+      if (!ostree_repo_write_commit_detached_metadata (self,
+                                                       checksum, detached_meta,
+                                                       cancellable, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+import_one_object_copy (OstreeRepo    *self,
+                        OstreeRepo    *source,
+                        const char   *checksum,
+                        OstreeObjectType objtype,
+                        GCancellable  *cancellable,
+                        GError        **error)
+{
+  gboolean ret = FALSE;
+  guint64 length;
+  gs_unref_object GInputStream *object = NULL;
+
+  if (!ostree_repo_load_object_stream (source, objtype, checksum,
+                                       &object, &length,
+                                       cancellable, error))
+    goto out;
+
+  if (objtype == OSTREE_OBJECT_TYPE_FILE)
+    {
+      if (!ostree_repo_write_content_trusted (self, checksum,
+                                              object, length,
+                                              cancellable, error))
+        goto out;
+    }
+  else
+    {
+      if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
+        {
+          if (!copy_detached_metadata (self, source, checksum, cancellable, error))
+            goto out;
+        }
+      if (!ostree_repo_write_metadata_stream_trusted (self, objtype,
+                                                      checksum, object, length,
+                                                      cancellable, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+static gboolean
+import_one_object_link (OstreeRepo    *self,
+                        OstreeRepo    *source,
+                        const char   *checksum,
+                        OstreeObjectType objtype,
+                        gboolean       *out_was_supported,
+                        GCancellable  *cancellable,
+                        GError        **error)
+{
+  gboolean ret = FALSE;
+  char loose_path_buf[_OSTREE_LOOSE_PATH_MAX];
+
+  _ostree_loose_path (loose_path_buf, checksum, objtype, self->mode);
+
+  if (!_ostree_repo_ensure_loose_objdir_at (self->objects_dir_fd, loose_path_buf, cancellable, error))
+    goto out;
+
+  *out_was_supported = TRUE;
+  if (linkat (source->objects_dir_fd, loose_path_buf, self->objects_dir_fd, loose_path_buf, 0) != 0)
+    {
+      if (errno == EEXIST)
+        {
+          ret = TRUE;
+        }
+      else if (errno == EMLINK || errno == EXDEV || errno == EPERM)
+        {
+          /* EMLINK, EXDEV and EPERM shouldn't be fatal; we just can't do the
+           * optimization of hardlinking instead of copying.
+           */
+          *out_was_supported = FALSE;
+          ret = TRUE;
+        }
+      else
+        ot_util_set_error_from_errno (error, errno);
+      
+      goto out;
+    }
+
+  if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
+    {
+      if (!copy_detached_metadata (self, source, checksum, cancellable, error))
+        goto out;
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
+/**
+ * ostree_repo_import_object_from:
+ * @self: Destination repo
+ * @source: Source repo
+ * @objtype: Object type
+ * @checksum: checksum
+ * @cancellable: Cancellable
+ * @error: Error
+ *
+ * Copy object named by @objtype and @checksum into @self from the
+ * source repository @source.  If both repositories are of the same
+ * type and on the same filesystem, this will simply be a fast Unix
+ * hard link operation.
+ *
+ * Otherwise, a copy will be performed.
+ */
+gboolean
+ostree_repo_import_object_from (OstreeRepo           *self,
+                                OstreeRepo           *source,
+                                OstreeObjectType      objtype,
+                                const char           *checksum, 
+                                GCancellable         *cancellable,
+                                GError              **error)
+{
+  gboolean ret = FALSE;
+  gboolean hardlink_was_supported = FALSE;
+      
+  if (self->mode == source->mode)
+    {
+      if (!import_one_object_link (self, source, checksum, objtype,
+                                   &hardlink_was_supported,
+                                   cancellable, error))
+        goto out;
+    }
+
+  if (!hardlink_was_supported)
+    {
+      gboolean has_object;
+
+      if (!ostree_repo_has_object (self, objtype, checksum, &has_object,
+                                   cancellable, error))
+        goto out;
+  
+      if (!has_object)
+        {
+          if (!import_one_object_copy (self, source, checksum, objtype,
+                                       cancellable, error))
+            goto out;
+        }
+    }
+
+  ret = TRUE;
+ out:
+  return ret;
+}
+
 /**
  * ostree_repo_query_object_storage_size:
  * @self: Repo
index de38ce43ab6449571879cc913793878cd9a70880..856b764ad72fc0dc52cc4aa454f3a148536c0052 100644 (file)
@@ -265,6 +265,13 @@ gboolean      ostree_repo_query_object_storage_size (OstreeRepo           *self,
                                                      GCancellable         *cancellable,
                                                      GError              **error);
 
+gboolean      ostree_repo_import_object_from (OstreeRepo           *self,
+                                              OstreeRepo           *source,
+                                              OstreeObjectType      objtype,
+                                              const char           *sha256, 
+                                              GCancellable         *cancellable,
+                                              GError              **error);
+
 gboolean      ostree_repo_delete_object (OstreeRepo           *self,
                                          OstreeObjectType      objtype,
                                          const char           *sha256, 
index 50b05a6f783a987d9376f787e779de9cfc4617ca..bd9773b70fef3b7706dc2898725b82c0cbcf42e4 100644 (file)
@@ -55,61 +55,6 @@ termination_condition (OtLocalCloneData  *self)
   return g_atomic_int_get (&self->n_objects_checked) == self->n_objects_to_check;
 }
 
-static gboolean
-import_one_object (OtLocalCloneData *data,
-                   const char   *checksum,
-                   OstreeObjectType objtype,
-                   GCancellable  *cancellable,
-                   GError        **error)
-{
-  gboolean ret = FALSE;
-  guint64 length;
-  gs_unref_object GInputStream *object = NULL;
-  
-  if (!ostree_repo_load_object_stream (data->src_repo, objtype, checksum,
-                                       &object, &length,
-                                       cancellable, error))
-    goto out;
-
-  if (objtype == OSTREE_OBJECT_TYPE_FILE)
-    {
-      if (!ostree_repo_write_content_trusted (data->dest_repo, checksum,
-                                              object, length,
-                                              cancellable, error))
-        goto out;
-    }
-  else
-    {
-      if (objtype == OSTREE_OBJECT_TYPE_COMMIT)
-        {
-          gs_unref_variant GVariant *detached_meta = NULL;
-          
-          if (!ostree_repo_read_commit_detached_metadata (data->src_repo,
-                                                          checksum, &detached_meta,
-                                                          cancellable, error))
-            goto out;
-
-          if (detached_meta)
-            {
-              if (!ostree_repo_write_commit_detached_metadata (data->dest_repo,
-                                                               checksum, detached_meta,
-                                                               cancellable, error))
-                goto out;
-            }
-        }
-      if (!ostree_repo_write_metadata_stream_trusted (data->dest_repo, objtype,
-                                                      checksum, object, length,
-                                                      cancellable, error))
-        goto out;
-    }
-
-  g_atomic_int_inc (&data->n_objects_copied);
-
-  ret = TRUE;
- out:
-  return ret;
-}
-
 static void
 import_one_object_thread (gpointer   object,
                           gpointer   user_data)
@@ -120,21 +65,16 @@ import_one_object_thread (gpointer   object,
   GError **error = &local_error;
   const char *checksum;
   OstreeObjectType objtype;
-  gboolean has_object;
   GCancellable *cancellable = NULL;
 
   ostree_object_name_deserialize (serialized_key, &checksum, &objtype);
 
-  if (!ostree_repo_has_object (data->dest_repo, objtype, checksum, &has_object,
-                               cancellable, error))
+  if (!ostree_repo_import_object_from (data->dest_repo, data->src_repo,
+                                       objtype, checksum, cancellable, error))
     goto out;
+  g_atomic_int_inc (&data->n_objects_copied);
 
-  if (!has_object)
-    {
-      if (!import_one_object (data, checksum, objtype, cancellable, error))
-        goto out;
-    }
-  
  out:
   g_atomic_int_add (&data->n_objects_checked, 1);
   if (termination_condition (data))